﻿using Arction.Wpf.Charting;
using Arction.Wpf.Charting.Axes;
using Arction.Wpf.Charting.ChartManager;
using Arction.Wpf.Charting.SeriesXY;
using Prism.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using WPF_UI_Lightningchart.ViewModels;

namespace WPF_UI_Lightningchart.UserControls
{
    /// <summary>
    /// AreaSpectrumControl.xaml 的交互逻辑
    /// </summary>
    public partial class AreaSpectrumControl : UserControl
    {
        private SensorViewModel SensorViewModel;
        private AreaSpectrumMonitor _lineSpectrumMonitors;//频谱
        private DispatcherTimer _timerAutoYFit;
        private ChartManager _chartManager;

        private bool IsAutoFit = true;

        //参数
        private int _intervalFFT { get; set; } = 1000; // FFT间隔
        private int _channelCount { get; set; } = 1; 
        private int _samplingFrequency { get; set; } = 1000; // 采样频率
        private int _resolution { get; set; } = 1024; // FFT窗口长度
        private int _topFrequency { get; set; } = 120; //最大频率

        public AreaSpectrumControl(IEventAggregator eventAggregator)
        {
            InitializeComponent();
            this.Loaded += (r, s) =>
            {
                _chartManager = new ChartManager
                {
                    Name = "chartManager",
                    MemoryGarbageCollecting = true
                };
                _timerAutoYFit = new DispatcherTimer
                {
                    Interval = new TimeSpan(0, 0, 0, 0, 500)
                };
                _timerAutoYFit.Tick += new EventHandler(_timerAutoYFit_Tick);
                _timerAutoYFit.IsEnabled = true;
                InitFFTCharts();
            };
            this.Unloaded += (s, e) =>
            {
                Task.Run(() =>
                {
                    //SensorViewModel.FFTProcessor.OnFFTCalculated -= HandleSignalReaderNewPoints;
                    this.Dispatcher.Invoke(() =>
                    {
                        PerformDisposing();
                    });
                });
            };
        }

        private void Grid_DragOver(object sender, DragEventArgs e)
        {
            e.Effects = e.Data.GetDataPresent(typeof(SensorViewModel)) ? DragDropEffects.Move : DragDropEffects.None;
            e.Handled = true;
        }
        private void Grid_Drop(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(typeof(SensorViewModel)))
            {
                if (SensorViewModel != null)
                {
                    SensorViewModel.FFTProcessor.OnFFTCalculated -= HandleSignalReaderNewPoints;
                }
                var sensorData = e.Data.GetData(typeof(SensorViewModel)) as SensorViewModel;
                SensorViewModel = (SensorViewModel)sensorData;
                SensorViewModel.FFTProcessor.OnFFTCalculated += HandleSignalReaderNewPoints;
                _intervalFFT = SensorViewModel._intervalFFT; // FFT间隔
                _channelCount = SensorViewModel._channelCount;
                _samplingFrequency = SensorViewModel._samplingFrequency; // 采样频率
                _resolution = SensorViewModel._resolution; // FFT窗口长度
                _topFrequency = SensorViewModel._topFrequency; //最大频率
                InitFFTCharts();
            }
        }

        private void HandleSignalReaderNewPoints(double[][][] xValues, double[][][] yValues, double speed)
        {
            Dispatcher.Invoke(new System.Action(() =>
            {
                int channelNumber = 0;
                if (xValues == null || xValues.Length == 0 || yValues == null || yValues.Length == 0) { return; }
                int rows = xValues.Length;
                for (int i = 0; i < xValues.Length; i++)
                {
                   if (_lineSpectrumMonitors != null)
                    {
                        _lineSpectrumMonitors.SetData(xValues[i][channelNumber], yValues[i][channelNumber]);//频谱
                    }
                }
            }));
        }

        public void PerformDisposing()
        {
            _timerAutoYFit.Tick -= new EventHandler(_timerAutoYFit_Tick);
            _timerAutoYFit.IsEnabled = false;
            _timerAutoYFit.Stop();
            gridChartGrid?.Children.Clear();
            gridChartGrid = null;
            if (_lineSpectrumMonitors != null)
            {
                _lineSpectrumMonitors.Dispose();
                _lineSpectrumMonitors = null;
            }
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }

        /// <summary>
        /// 初始化FFT
        /// </summary>
        private void InitFFTCharts()
        {
            //清除释放资源

            if (_lineSpectrumMonitors != null)
            {
                _lineSpectrumMonitors.Dispose();
                _lineSpectrumMonitors = null;
            }
            int resolution = _resolution;
            //满足采样定理
            if (_topFrequency < _samplingFrequency / 2)
                resolution = (int)Math.Round((double)_topFrequency / (double)(_samplingFrequency) * _resolution);//FFT显示窗口长度

            AreaSpectrumMonitor asm =
                new AreaSpectrumMonitor(gridChartGrid,
                    resolution,
                    _topFrequency,
                    "频谱",
                    //_chart.ViewXY.SampleDataSeries[channelNumber].LineStyle.Color
                    Colors.Blue
                );

            asm.Chart.ChartName = "Area Spectrum Chart " + (0).ToString();

            _lineSpectrumMonitors = asm;

            _lineSpectrumMonitors.SetBounds();
            //ArrangeFFTCharts();
        }
        //private void ArrangeFFTCharts()
        //{
        //    if (_channelCount == 0)
        //        return;

        //    int totalWidth = (int)gridChartGrid.ActualWidth;
        //    int totalHeight = (int)gridChartGrid.ActualHeight;
        //    int height = totalHeight / _channelCount;
        //    int x = 0;
        //    int top = 0;
        //    _lineSpectrumMonitors.SetBounds();
        //    top += height;
        //}
        private void _timerAutoYFit_Tick(object sender, EventArgs e)
        {
            if(IsAutoFit) FitY();
        }
        private void FitY()
        {
            if (_lineSpectrumMonitors != null)
            {
                _lineSpectrumMonitors.FitView();
            }
        }
    }

    //频谱
    public class AreaSpectrumMonitor
    {
        [System.Runtime.InteropServices.DllImport("user32")]
        private static extern bool SetWindowPos(
            IntPtr hWnd,
            IntPtr hWndInsertAfter,
            int x,
            int y,
            int cx,
            int cy,
            uint uFlags
        );

        private LightningChartUltimate _chart;
        private int m_iResolution;

        public AreaSpectrumMonitor(
            Panel parentControl,
            int resolution,
            double xAxisMax,
            string title,
            Color lineColor
        )
        {
            m_iResolution = resolution;

            _chart = new LightningChartUltimate
            {
                ChartName = "Area spectrum chart",
                VerticalAlignment = VerticalAlignment.Stretch,
                HorizontalAlignment = HorizontalAlignment.Stretch
            };

            _chart.BeginUpdate();

            //Setup custom style
            _chart.ChartBackground.Style = RectFillStyle.ColorOnly;
            _chart.ChartBackground.Color = Color.FromArgb(0, 0, 0, 0);
            _chart.ChartBackground.GradientColor = Color.FromArgb(0, 0, 0, 0);
            _chart.ChartBackground.GradientFill = GradientFill.Solid;
            _chart.Title.Text = title;
            _chart.Title.Color = lineColor;
            _chart.Title.Font = new WpfFont("Segoe UI", 14.0, true, false);
            _chart.Title.Offset.SetValues(0, -20);
            _chart.ColorTheme = ColorTheme.LightGray;

            _chart.ViewXY.GraphBackground.Style = RectFillStyle.ColorOnly;
            _chart.ViewXY.GraphBackground.Color = Color.FromArgb(0, 0, 0, 0);
            _chart.ViewXY.GraphBackground.GradientColor = Color.FromArgb(0, 0, 0, 0);
            _chart.ViewXY.GraphBackground.GradientFill = GradientFill.Solid;
            _chart.ViewXY.LegendBoxes[0].Visible = false;
            _chart.ViewXY.Border.Visible = false;
            _chart.ViewXY.AxisLayout.YAxesLayout = YAxesLayout.Layered;
            _chart.ViewXY.AxisLayout.AutoAdjustMargins = false;

            AxisX axisX = _chart.ViewXY.XAxes[0];
            axisX.SetRange(0, xAxisMax);
            axisX.Title.Font = new WpfFont("Segoe UI", 13.0, true, false);
            axisX.Title.Visible = true;
            axisX.Title.Text = "Frequency (Hz)";
            axisX.Units.Visible = false;
            axisX.ValueType = AxisValueType.Number;
            axisX.Position = 100;
            axisX.LabelsPosition = Alignment.Far;
            axisX.MajorDivTickStyle.Alignment = Alignment.Far;
            axisX.MinorDivTickStyle.Alignment = Alignment.Far;
            axisX.MajorDivTickStyle.Color = Colors.Gray;
            axisX.MinorDivTickStyle.Color = Colors.DimGray;
            axisX.LabelsColor = Colors.Black;
            axisX.LabelsFont = new WpfFont("Segoe UI", 13, false, false);
            axisX.ScrollMode = XAxisScrollMode.None;

            AxisY axisY = _chart.ViewXY.YAxes[0];
            axisY.MajorDivTickStyle.Color = Colors.Gray;
            axisY.MinorDivTickStyle.Color = Colors.DimGray;
            axisY.AutoFormatLabels = false;
            axisY.LabelsNumberFormat = "0";
            axisY.SetRange(0, 7000);
            axisY.Title.Font = new WpfFont("Segoe UI", 13.0, true, false);
            axisY.Title.Visible = true;
            axisY.Title.Text = "Amplitude (mV)";
            axisY.LabelsColor = Colors.Black;
            axisY.LabelsFont = new WpfFont("Segoe UI", 13, false, false);
            axisY.Units.Visible = false;

            AreaSeries areaSeries = new AreaSeries(_chart.ViewXY, axisX, axisY);
            areaSeries.Title.Visible = false;
            areaSeries.LineStyle.Color = lineColor;
            areaSeries.LineStyle.Width = 1f;
            areaSeries.Fill.Color = ChartTools.CalcGradient(lineColor, Colors.AliceBlue, 50);
            areaSeries.Fill.GradientFill = GradientFill.Solid;
            //areaSeries.AllowUserInteraction = false;
            areaSeries.PointsVisible = false;

            _chart.ViewXY.AreaSeries.Add(areaSeries);

            _chart.EndUpdate();

            parentControl.Children.Add(_chart);
        }

        public LightningChartUltimate Chart
        {
            get
            {
                return _chart;
            }
        }

        public void FitView()
        {
            _chart.ViewXY.ZoomToFit();
        }

        public void Dispose()
        {
            if (_chart != null)
            {
                _chart.Dispose();
                _chart = null;
            }
        }

        public void SetData(double[] xValues, double[] yValues)
        {
            // Only accept resolution count of data points.

            if (xValues.Length > m_iResolution)//长度以设定的分辨率为准
            {
                Array.Resize(ref xValues, m_iResolution);
                //此方法分配一个具有指定大小的新数组，将元素从旧数组复制到新数组，然后用新数组替换旧数组。
                //如果 array 为 null，此方法将新建具有指定大小的数组。
                //public static void Resize(
                //ref T[] array,
                //int newSize)
            }

            if (yValues.Length > m_iResolution)//长度以设定的分辨率为准
            {
                Array.Resize(ref yValues, m_iResolution);
            }

            _chart.BeginUpdate();

            // Set data to area series.

            int pointsCount = xValues.Length;
            AreaSeriesPoint[] aPoints = new AreaSeriesPoint[pointsCount];
            for (int i = 0; i < pointsCount; i++)
            {
                aPoints[i].X = xValues[i];
                aPoints[i].Y = yValues[i];
            }

            _chart.ViewXY.AreaSeries[0].Points = aPoints;

            _chart.EndUpdate();
        }

        public void SetBounds( )
        {
            _chart.Margin = new Thickness(10);
            _chart.HorizontalAlignment = HorizontalAlignment.Stretch;
            _chart.VerticalAlignment = VerticalAlignment.Stretch;
        }
    }

}
